package com.na.flipcard;

import java.util.List;
import java.util.Random;
import java.util.Vector;

import com.na.flipcard.ui.GameOverUI;
import com.na.flipcard.ui.GameUI;
import com.na.flipcard.ui.PauseUI;
import com.na.flipcard.util.GameConfigEx;
import com.na.game.GameManager;
import com.na.game.event.Updater;
import com.na.game.sound.SoundManager;
import com.na.game.ui.UIManager;
import com.na.game.util.PList;
import com.na.game.util.PListManager;
import com.na.game.util.Timer;
import com.na.game.util.TimerTask;
import com.na.game.util.Util;

public class Game implements Updater, Effect {

	private static final byte DATA_NULL = -1;
	private static final byte DATA_BINGO = -2;
	
	private int score;
	private int level;
	private int startTime; // ms
	private int timeLimit;
	private int timeRemain;
	private int[] selectedGridIndex;
	private PList plist;
	private byte[] data; // -1->null, -2->bingo, >=0 plist index
	private byte step;
	
	private int tick;
	private int state;
	private static final int STATE_NORMAL = 0;
	private static final int STATE_BAN_TOUCH = 1;
	
	private boolean dither;
	private int ditherDegrees;
	private boolean ditherDegreesPlus;
	
	private GameUI ui;
	private Effect effect;
	private Timer warnTimer;
	private boolean paused;
	private long pausedTime;
	
	public Effect hold;
	
	public Game(GameUI ui, int timeLimit) {
		this.ui = ui;
		this.timeLimit = timeLimit;
		timeRemain = timeLimit;
		selectedGridIndex = new int[2];
		data = new byte[34];
		init(1);
	}
	
	private void init(int lvl) {
		level = lvl;
		selectedGridIndex[0] = selectedGridIndex[1] = -1;
		step = 0;
		tick = 0;
		state = STATE_NORMAL;
		dither = false;
		ditherDegrees = 0;
		ditherDegreesPlus = true;
		stopWarnTimer();
		
		String[] plists = new String[]{GameConfigEx.RES_PLIST_BIG_ICON_1, GameConfigEx.RES_PLIST_BIG_ICON_2, GameConfigEx.RES_PLIST_BIG_ICON_3};
		String plistFile = plists[new Random().nextInt(plists.length)];
		GameManager.get(PListManager.class).loadPList(plistFile);
		plist = GameManager.get(PListManager.class).getPList(plistFile);
		
		int size = GameConfigEx.GAME_ENTRY_SIZE[lvl - 1 < GameConfigEx.GAME_ENTRY_SIZE.length ? lvl - 1 : GameConfigEx.GAME_ENTRY_SIZE.length - 1];
		List<Integer> entrys = Util.getAscendIntsList(0, 1, plist.size());
		int pro = GameConfigEx.GAME_ENTRY_REMOVE_PRO[lvl - 1 < GameConfigEx.GAME_ENTRY_REMOVE_PRO.length ? lvl - 1 : GameConfigEx.GAME_ENTRY_SIZE.length - 1];
		Vector<Byte> indices = new Vector<Byte>(size);
		for (int i = 0; i < size; i++) {
			int idx = new Random().nextInt(entrys.size());
			byte index;
			if (new Random().nextInt(100) < pro) {
				index = (byte) entrys.remove(idx).intValue();
			} else {
				index = (byte) entrys.get(idx).intValue();
			}
			indices.addElement(index);
			indices.addElement(index);
		}
		for (int i = data.length - 1; i >= 0; i--) {
			int idx = new Random().nextInt(i + 1);
			byte d = DATA_NULL;
			if (idx < indices.size()) {
				d = indices.remove(idx);
			}
			data[i] = d;
		}
		
		if (lvl == 1) {
			startTime = (int) System.currentTimeMillis();
		} else {
			timeLimit += 5000;
		}
	}
	
	@Override
	public boolean isSelected(int gridIndex) {
		if (effect != null) {
			return effect.isSelected(gridIndex);
		}
		return gridIndex == selectedGridIndex[0] || gridIndex == selectedGridIndex[1];
	}
	
	public boolean isBingo(int index) {
		return data[index] == DATA_BINGO;
	}
	
	public byte[] getData() {
		return data;
	}
	
	public PList getPList() {
		return plist;
	}
	
	public int getScore() {
		return score * getMul();
	}
	
	public String getScoreTxt() {
		if (hold != null) {
			return hold.getScoreTxt();
		}
		return String.valueOf(score);
	}
	
	public int getLevel() {
		return level;
	}
	
	public int getTimeLimit() {
		return timeLimit;
	}
	
	public boolean addTime(int millis) {
		if (millis > 0) {
			timeLimit += millis;
			return true;
		}
		return false;
	}
	
	public int getTimeRemain() {
		return timeRemain;
	}
	
	public void bingo(int idx1, int idx2) {
		if (idx1 >= 0 && idx2 >= 0 &&
				idx1 < data.length && idx2 < data.length &&
				data[idx1] >= 0 && data[idx2] >= 0 &&
				data[idx1] == data[idx2]) {
			data[idx1] = data[idx2] = DATA_BINGO;
			score += GameConfigEx.GAME_SCORES[level - 1 < GameConfigEx.GAME_SCORES.length ? level - 1 : GameConfigEx.GAME_SCORES.length - 1];
			if (level >= GameConfigEx.GAME_SCORES.length) {
				score += new Random().nextInt(10);
			}
			GameManager.get(SoundManager.class).play(GameConfigEx.RES_SOUND_BINGO);
		}
	}
	
	public void onTouch(int gridIndex) {
		if (state == STATE_BAN_TOUCH || data[gridIndex] < 0 || gridIndex == selectedGridIndex[1 - step]) {
			return;
		}
		selectedGridIndex[step] = gridIndex;
		if (step == 0) {
			step = 1;
		} else {
			step = 0;
			if (data[selectedGridIndex[0]] == data[gridIndex]) { // bingo
				bingo(selectedGridIndex[0], gridIndex);
			}
			
			state = STATE_BAN_TOUCH;
		}
	}

	@Override
	public boolean isDither() {
		if (effect != null) {
			return effect.isDither();
		}
		return dither;
	}
	
	public int getDitherDegrees() {
		return ditherDegrees;
	}
	
	public void pause() {
		if (!paused) {
			GameManager.get(UIManager.class).pushUI(new PauseUI(this));
			paused = true;
			pausedTime = System.currentTimeMillis();
		}
	}
	
	public void resume() {
		if (paused) {
			paused = false;
			startTime += System.currentTimeMillis() - pausedTime;
		}
	}
	
	@Override
	public void update() {
		if (paused) {
			return;
		}
		if (timeRemain <= 0) {
			if (stopWarnTimer()) {
				GameManager.get(UIManager.class).pushUI(new GameOverUI(this));
				GameManager.get(SoundManager.class).play(GameConfigEx.RES_SOUND_WIND);
			}
			return;
		}
		timeRemain = (int) (timeLimit - (System.currentTimeMillis() - startTime));
		if (timeRemain <= 4000) {
			dither = true;
			if (warnTimer == null) {
				warnTimer = new Timer();
				warnTimer.schedule(new TimerTask() {
					@Override
					public void execute() {
						GameManager.get(SoundManager.class).play(GameConfigEx.RES_SOUND_WARN);
					}
				}, 0, 1000);
			}
		}
		if (isDither()) {
			if (ditherDegreesPlus) {
				ditherDegrees += 3;
			} else {
				ditherDegrees -= 3;
			}
			if (ditherDegrees >= 15) {
				ditherDegreesPlus = false;
			} else if (ditherDegrees <= -15) {
				ditherDegreesPlus = true;
			}
		}
		if (state == STATE_BAN_TOUCH) {
			if (tick < 3) {
				tick++;
			} else {
				tick = 0;
				selectedGridIndex[0] = selectedGridIndex[1] = -1;
				state = STATE_NORMAL;
			}
		}
		if (shouldLevelUp()) {
			levelUp();
		}
	}
	
	private boolean shouldLevelUp() {
		for (byte b : data) {
			if (b >= 0) {
				return false;
			}
		}
		return true;
	}
	
	private void levelUp() {
		init(level + 1);
		ui.syncUI();
	}
	
	public void setEffect(Effect effect) {
		this.effect = effect;
	}
	
	public Effect getEffect() {
		return effect;
	}
	
	private boolean stopWarnTimer() {
		if (warnTimer != null) {
			warnTimer.cancel();
			warnTimer = null;
			return true;
		}
		return false;
	}

	@Override
	public int getMul() {
		if (hold != null) {
			return hold.getMul();
		}
		return 1;
	}
	
}
